package org.example;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.example.crm.clue.Result;
import org.example.crm.common.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

@Component
@Slf4j
public class AuthFilter implements GlobalFilter {
    @Autowired
    private AuthProperties authProperties;
    @Autowired
    private RedisService redisService;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        String method = request.getMethodValue();
        String token = request.getHeaders().getFirst("token");
        if (log.isDebugEnabled()) {
            log.debug("{},{},{}", path, method, token);
        }
        // 白名单
        if(authProperties.getWhiteList().contains(path)){
            return chain.filter(exchange);
        }
        // token检查
        if(!StringUtils.hasText(token)){
            return error(exchange,401,"token不能为空");
        }
        // 权限检查
        if(!hasPerms(path,method,token)){
            return error(exchange,403,"无权访问");
        }
        // 放行
        return chain.filter(exchange);
    }

    private boolean hasPerms(String path, String method, String token) {
        String apiPath = redisService.get(RedisService.TOKEN_PREFIX + token);
        if (!StringUtils.hasText(apiPath)) {
            return false;
        }
        String api = method + "_" + path;
        List<String> patterns = Arrays.asList(apiPath.split(","));
        for (String pattern : patterns) {
            if (antPathMatcher.match(pattern, api)) {
                return true;
            }
        }
        return false;
    }

    @SneakyThrows
    private Mono<Void> error(ServerWebExchange exchange, int code, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");

        String json = new ObjectMapper().writeValueAsString(Result.error(code, msg));
        DataBuffer wrap = response.bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(wrap));
    }

    public static void main(String[] args) {
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        String pattern = "/clues/{id}";
        System.out.println("antPathMatcher.match(pattern, \"/clues/111\") = " + antPathMatcher.match(pattern, "/clues/111"));
        System.out.println("antPathMatcher.match(pattern, \"/clues\") = " + antPathMatcher.match(pattern, "/clues"));
    }
}
